
/*
 *
 * Copyright (C) 2008 Mattias Blomqvist, patr-blo at dsv dot su dot se
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;

namespace ClientExample.Controls
{
    public class RichTextBoxExtended : RichTextBox
    {
        public new event LinkClickedEventHandler LinkClicked;
        protected bool isHoveringLink = false;
        protected string strHovereringLink = null;
        protected List<String> lKnownProtocols = new List<String>();

        [DefaultValue(true)]
        public new bool DetectUrls
        {
            get { return base.DetectUrls; }
            set 
            {
                if (!value)
                    isHoveringLink = false;
                base.DetectUrls = value;
            }
        }

        [Editor("System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(System.Drawing.Design.UITypeEditor))]
        public List<String> KnownProtocols
        {
            get { return lKnownProtocols; }
            set { lKnownProtocols = value; }
        }

        public RichTextBoxExtended()
        {
            LinkClicked = new LinkClickedEventHandler(OnLinkClicked);
            this.MouseMove += new MouseEventHandler(RichTextBoxExtended_MouseMove);
            this.MouseClick += new MouseEventHandler(RichTextBoxExtended_MouseClick);
            base.LinkClicked += new LinkClickedEventHandler(RichTextBoxExtended_LinkClicked);
        }

        void OnLinkClicked(object sender, LinkClickedEventArgs e) { }
        void RichTextBoxExtended_LinkClicked(object sender, LinkClickedEventArgs e)
        {
            LinkClicked(sender, e);
        }

        void RichTextBoxExtended_MouseClick(object sender, MouseEventArgs e)
        {
            if (isHoveringLink)
            {
                LinkClicked(this, new LinkClickedEventArgs(strHovereringLink));
            }
        }

        void RichTextBoxExtended_MouseMove(object sender, MouseEventArgs e)
        {
			try
			{
	            if (!DetectUrls)
	                return;
	            /****
	             * We want to be able to handle none standard protocol urls.
	             ****/
	            int posWhereMouse = this.GetCharIndexFromPosition(e.Location);
	            int start = posWhereMouse;
	            int end = posWhereMouse;
	            bool valid = true;
	            // get start
	            do
	            {
	                start--;
	            }
	            while (start > 0 && isValidCharforLink(this.Text[start]));
	            if (start == 0)
	                valid = false;
	            start++;
	            // No need to look for end point if start point is not valid
	            if (valid)
	            {
	                do
	                {
	                    end++;
	                }
	                while (this.TextLength > end && isValidCharforLink(this.Text[end]));
	            }
	            if (this.TextLength == end)
	                valid = false;
	            // No need to look for end point if end point is not valid
	            string tmp = this.Text.Substring(start, end - start);
	            if (valid)
	            {
	                string link = this.Text.Substring(start, end - start);
	                if ((end = link.IndexOf("://")) != -1)
	                {
	                    string str = link.Substring(0, end);
	                    if (lKnownProtocols.Contains(str))
	                    {
	                        if (!isHoveringLink)
	                        {
	                            isHoveringLink = true;
	                            strHovereringLink = link;
	                            this.Cursor = System.Windows.Forms.Cursors.Hand;
	                        }
	                    }
	                    else
	                    {
	                        valid = false;
	                    }
	                }
	                else
	                {
	                    valid = false;
	                }
	            }

	            if (!valid && isHoveringLink)
	            {
	                this.Cursor = System.Windows.Forms.Cursors.Default;
	                isHoveringLink = false;
	            }
			}catch(System.ArgumentOutOfRangeException)
			{
				// Mono can't handle when mouse is over a area that doesn't contain any text
			}
        }
		
		public void FindUrl(ref int start, ref int end)
		{
            try
            {
                /****
                 * We want to be able to handle none standard protocol urls.
                 ****/
                //int posWhereMouse = this.GetCharIndexFromPosition(e.Location);
                //int start = posWhereMouse;
                //int end = posWhereMouse;
                int pos;
                bool valid = true;
                // get start
                do
                {
                    start--;
                }
                while (start > 0 && isValidCharforLink(this.Text[start]));
                if (start < 0)
                    start = 0;
                else
                    start++;

                end = start;
                // No need to look for end point if start point is not valid
                while (this.TextLength > (end) && isValidCharforLink(this.Text[end]))
                {
                    end++;
                }
                if (this.TextLength < end)
                    end = this.TextLength;
                // No need to look for end point if end point is not valid
                string link = this.Text.Substring(start, end - start);
                if ((pos = link.IndexOf("://")) != -1)
                {
                    string str = link.Substring(0, pos);
                    if (lKnownProtocols.Contains(str))
                    {
                        valid = true;
                    }
                    else
                    {
                        valid = false;
                    }
                }
                else
                {
                    valid = false;
                }

                if (!valid)
                {
                    end = 0;
                }
                else
                {
                    end = end - start;
                }
            }
            catch
            {
                end = 0;
            }
		}

        protected bool isValidCharforLink(char c)
        {
            if (char.IsLetterOrDigit(c))
                return true;
            else if (char.IsPunctuation(c))
                return true;
            else if (c.Equals(':'))
                return true;
            else if (c.Equals('/'))
                return true;
            else
                return false;
        }
    }
}
